﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Text.RegularExpressions;



namespace GoScreenSaver
{
    public delegate bool CallBackPtr(IntPtr hwnd, IntPtr lParam);
    public delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);

    public struct WindowHwndTitleRegex
    {
        public IntPtr hwnd;
        public Regex re;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;

        public POINT(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }

        public static implicit operator System.Drawing.Point(POINT p)
        {
            return new System.Drawing.Point(p.X, p.Y);
        }

        public static implicit operator POINT(System.Drawing.Point p)
        {
            return new POINT(p.X, p.Y);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct MSLLHOOKSTRUCT
    {
        public POINT pt;
        public int mouseData; // be careful, this must be ints, not uints (was wrong before I changed it...). regards, cmew.
        public int flags;
        public int time;
        public UIntPtr dwExtraInfo;
    }

    public enum HookType : int
    {
        WH_JOURNALRECORD = 0,
        WH_JOURNALPLAYBACK = 1,
        WH_KEYBOARD = 2,
        WH_GETMESSAGE = 3,
        WH_CALLWNDPROC = 4,
        WH_CBT = 5,
        WH_SYSMSGFILTER = 6,
        WH_MOUSE = 7,
        WH_HARDWARE = 8,
        WH_DEBUG = 9,
        WH_SHELL = 10,
        WH_FOREGROUNDIDLE = 11,
        WH_CALLWNDPROCRET = 12,
        WH_KEYBOARD_LL = 13,
        WH_MOUSE_LL = 14
    }

    public partial class Form1 : Form
    {
        private const int SM_CXSCREEN = 0;
        private const int SM_CYSCREEN = 1;
        private const int SM_CXVIRTUALSCREEN = 78;
        private const int SM_CYVIRTUALSCREEN = 79;

        [DllImport("user32.dll", EntryPoint = "GetSystemMetrics")]
        public static extern int GetSystemMetrics(int which);

        [DllImport("User32.dll")]
        static extern int SetForegroundWindow(IntPtr hWnd);

        [DllImport("User32.dll")]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        static extern IntPtr GetForegroundWindow();

        public static IntPtr HWND_TOPMOST = (IntPtr)(-1);
        public static IntPtr HWND_TOP = (IntPtr)(0);
        public static IntPtr HWND_NOTOPMOST = (IntPtr)(-2);

        public const int SWP_SHOWWINDOW = 0x0040;
        private const int SW_HIDE = 0;
        private const int SW_SHOW = 1;

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, UInt32 uFlags);

        [DllImport("user32.dll")]
        static extern IntPtr GetActiveWindow();

        [DllImport("user32.dll")]
        public static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern int GetWindowTextLength(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern int EnumWindows(CallBackPtr callPtr, IntPtr lPar);

        [DllImport("user32.dll")]
        private static extern int ShowWindow(IntPtr hwnd, int command);

        [DllImport("user32.dll")]
        static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadID);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll")]
        static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam,
           IntPtr lParam);

        [DllImport("user32.dll")]
        static extern bool SetCursorPos(int X, int Y);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        public Form1()
        {
            InitializeComponent();
        }

        Point mouseLocation;
        String gn = "";
        String ginfo = "";
        TcpClient cli = new TcpClient("igs.joyjoy.net", 6969);
        NetworkStream s;
        IntPtr llkbProcHdl, llmProcHdl;
        HookProc llkbProc, llmProc;

        private void Form1_Load(object sender, EventArgs e)
        {
            s = cli.GetStream();
            StringBuilder ss = new StringBuilder();
            int bytesRead;
            byte[] buffer = new byte[256];
            byte[] ba;
            ASCIIEncoding asen = new ASCIIEncoding();

            // Read response from server.
            do
            {
                bytesRead = s.Read(buffer, 0, buffer.Length);
                ss.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));
            }
            while (bytesRead == 256 || !ss.ToString().Contains("Login:"));

            ba = asen.GetBytes("guest\n");
            s.Write(ba, 0, ba.Length);
            do
            {
                bytesRead = s.Read(buffer, 0, buffer.Length);
                ss.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));
            }
            while (bytesRead == 256 || !ss.ToString().Contains("#>"));

            GetTopRankGameNumber();
            SetCursorPos(GetSystemMetrics(SM_CXVIRTUALSCREEN), GetSystemMetrics(SM_CYVIRTUALSCREEN));
            Cursor.Hide();
            WindowState = FormWindowState.Maximized;
            Show();
            LaunchIGS();
            timer1.Enabled = true;
            timer1.Start();
        }

        private void LaunchIGS()
        {
            process1.Start();
            process1.WaitForInputIdle();
            SetActiveWindow(process1.MainWindowHandle);
            SetForegroundWindow(process1.MainWindowHandle);
            SendKeys.SendWait("{ENTER}");
            IntPtr hdl;

            while ((hdl = FindWindowMatchesTitle(new Regex("on IGS-PandaNet"))) == IntPtr.Zero)
            {
                Thread.Sleep(100);
            }
            SetForegroundWindow(hdl);
            process1.WaitForInputIdle();
            Thread.Sleep(1000);
            SendKeys.SendWait("{F7}");
            Thread.Sleep(500);
            SendKeys.SendWait("observe " + gn + "{ENTER}");
            //SendKeys.SendWait("{ENTER}");

            IntPtr GameWindowHdl;
            while ((GameWindowHdl = FindWindowMatchesTitle(new Regex("Game " + gn))) == IntPtr.Zero)
            {
                Thread.Sleep(100);
            }
            SetWindowPos(GameWindowHdl, HWND_TOPMOST, 0, 0, GetSystemMetrics(SM_CXSCREEN),
                               GetSystemMetrics(SM_CYSCREEN), SWP_SHOWWINDOW);
        }

        private void GetTopRankGameNumber()
        {
            StringBuilder ss = new StringBuilder();
            int bytesRead;
            byte[] buffer = new byte[256];
            byte[] ba;
            ASCIIEncoding asen = new ASCIIEncoding();

            ba = asen.GetBytes("games\n");
            s.Write(ba, 0, ba.Length);

            ss = new StringBuilder();
            do
            {
                bytesRead = s.Read(buffer, 0, buffer.Length);
                ss.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));
            }
            while (bytesRead == 256 || !ss.ToString().Contains("#>"));
            StringReader sr = new StringReader(ss.ToString());
            sr.ReadLine();
            String g, tgn = "", twr = "", wr = "";
            while ((g = sr.ReadLine()) != null)
            {
                try
                {
                    tgn = g.Substring(g.IndexOf("[") + 1, g.IndexOf("]") - g.IndexOf("[") - 1).Trim();
                    twr = g.Substring(g.IndexOf("[", 2) + 1, g.IndexOf("]", g.IndexOf("[", 2)) - g.IndexOf("[", 2) - 1).Trim();
                    if (DigitizeRank(twr) >= DigitizeRank(wr))
                    {
                        wr = twr;
                        gn = tgn;
                        ginfo = g.Substring(0, g.IndexOf("vs."));
                    }
                }
                catch (Exception)
                {
                }
            }
        }

        private bool IsCurrentGameExists()
        {
            StringBuilder ss = new StringBuilder();
            int bytesRead;
            byte[] buffer = new byte[256];
            byte[] ba;
            ASCIIEncoding asen = new ASCIIEncoding();

            ba = asen.GetBytes("games " + gn + "\n");
            s.Write(ba, 0, ba.Length);

            ss = new StringBuilder();
            do
            {
                bytesRead = s.Read(buffer, 0, buffer.Length);
                ss.Append(Encoding.ASCII.GetString(buffer, 0, bytesRead));
            }
            while (bytesRead == 256 || !ss.ToString().Contains("#>"));
            return (ss.ToString().Contains(ginfo));
        }

        private double DigitizeRank(String a)
        {
            Match m = Regex.Match(a, @"(\d+)(k|d)(\*?)");
            if (m.Success)
            {
                return Int32.Parse(m.Groups[1].Value) * (m.Groups[2].Value == "d" ? 1 : -1) + (m.Groups[3].Value == "*" ? 0.5 : 0);
            }
            return -100;
        }


        private IntPtr LowLevelKeyboardProc(int code, IntPtr wParam, IntPtr lParam)
        {
            if (code < 0)
            {
                //you need to call CallNextHookEx without further processing
                //and return the value returned by CallNextHookEx
                return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
            }
            // we can convert the 2nd parameter (the key code) to a System.Windows.Forms.Keys enum constant
            //Keys keyPressed = (Keys)wParam.ToInt32();
            //Console.WriteLine(keyPressed);
            AsyncQuit();
            //return the value returned by CallNextHookEx
            return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }

        private IntPtr LowLevelMouseProc(int code, IntPtr wParam, IntPtr lParam)
        {
            if (code < 0)
            {
                //you need to call CallNextHookEx without further processing
                //and return the value returned by CallNextHookEx
                return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
            }
            try
            {
                if (wParam.ToInt64() == 0x0200)
                {
                    MSLLHOOKSTRUCT ms = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
                    if (!mouseLocation.IsEmpty)
                    {
                        // Terminate if mouse is moved a significant distance
                        if (Math.Abs(mouseLocation.X - ms.pt.X) > 5 ||
                            Math.Abs(mouseLocation.Y - ms.pt.Y) > 5)
                            AsyncQuit();
                    }

                    // Update current mouse location
                    mouseLocation = ms.pt;
                }
                else
                {
                    AsyncQuit();
                }
            }
            catch (Exception)
            {
                AsyncQuit();
            }
            //return the value returned by CallNextHookEx
            return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
        }

        void AsyncQuit()
        {
            Thread t = new Thread(new ThreadStart(QuitApp));
            t.Start();
        }

        void QuitApp()
        {
            Application.Exit();
        }

        public IntPtr FindWindowMatchesTitle(Regex re)
        {
            var callBackPtr = new CallBackPtr(FindWindowMatchesTitle);
            WindowHwndTitleRegex s = new WindowHwndTitleRegex();
            s.re = re;
            IntPtr strPtr = Marshal.AllocHGlobal(Marshal.SizeOf(s));
            Marshal.StructureToPtr(s, strPtr, true);
            EnumWindows(callBackPtr, strPtr);
            WindowHwndTitleRegex r = (WindowHwndTitleRegex)Marshal.PtrToStructure(strPtr, typeof(WindowHwndTitleRegex));
            return r.hwnd;
        }
        public bool FindWindowMatchesTitle(IntPtr hwnd, IntPtr lParam)
        {
            // Allocate correct string length first
            int length = GetWindowTextLength(hwnd);
            StringBuilder sb = new StringBuilder(length + 1);
            GetWindowText(hwnd, sb, sb.Capacity);
            WindowHwndTitleRegex r = (WindowHwndTitleRegex)Marshal.PtrToStructure(lParam, typeof(WindowHwndTitleRegex));

            if (r.re.IsMatch(sb.ToString()))
            {
                Console.WriteLine(sb.ToString());
                r.hwnd = hwnd;
                Marshal.StructureToPtr(r, lParam, true);
                return false;
            }
            return true;
        }

        private void Form1_Shown(object sender, EventArgs e)
        {
            SetWindowPos(Handle, HWND_NOTOPMOST, 0, 0, GetSystemMetrics(SM_CXVIRTUALSCREEN),
                               GetSystemMetrics(SM_CYVIRTUALSCREEN), SWP_SHOWWINDOW);
            llkbProc = new HookProc(LowLevelKeyboardProc);
            llkbProcHdl = SetWindowsHookEx(HookType.WH_KEYBOARD_LL, llkbProc, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
            llmProc = new HookProc(LowLevelMouseProc);
            llmProcHdl = SetWindowsHookEx(HookType.WH_MOUSE_LL, llmProc, GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName), 0);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            timer1.Stop();
            if (!IsCurrentGameExists())
            {
                if (!process1.HasExited) process1.Kill();
                while (!process1.HasExited)
                {
                    Thread.Sleep(100);
                }
                GetTopRankGameNumber();
                LaunchIGS();
            }
            timer1.Start();
        }
    }
}
